﻿/* Author: Achim Schnell */

using System;
using System.Linq;
using System.Collections.Generic;

namespace AppFx.Tokenization
{
    /// <summary>
    /// Verifiers numbers for a token.
    /// </summary>
    public class NumberVerifier : Verifier
    {
        #region Fields

        private char[] startChars = new char[] { '+', '-', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
        private char[] numChars = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };

        #endregion

        #region Properties

        public bool RemovePositiveSign { get; set; }

        public char DecimalChar { get; set; }
        public char ThousandSeparator { get; set; }

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="NumberVerifier"/> class.
        /// </summary>
        /// <param name="tokenizer">The parent tokenizer.</param>
        public NumberVerifier(ITokenizer tokenizer) : this(tokenizer, true, ',', '.') { }

        /// <summary>
        /// Initializes a new instance of the <see cref="NumberVerifier"/> class.
        /// </summary>
        /// <param name="tokenizer">The parent tokenizer.</param>
        /// <param name="removePositiveSign">Ff set to <c>true</c> Token will be stripped of the PositiveSign .</param>
        /// <param name="decimalChar">The decimal char.</param>
        /// <param name="thousandSeparator">The thousands separator.</param>
        public NumberVerifier(ITokenizer tokenizer, bool removePositiveSign, char decimalChar, char thousandSeparator) : base(tokenizer)
        {
            RemovePositiveSign = removePositiveSign;
            DecimalChar = decimalChar;
            ThousandSeparator = thousandSeparator;
        }

        #endregion

        #region Verifier

        /// <summary>
        /// Detects if the given char matches the token's pattern.
        /// </summary>
        /// <param name='c'>Char to verify for tokenization.</param>
        /// <returns>
        /// A bool indicating if the given char matches the token's pattern.
        /// </returns>
        public override bool Verify(char c)
        {
            char? next = PreviewNext();
            IToken previous = ReviewLast();
            bool deny = true;
            if (previous != null)
            {
                IToken visit = null;
                IToken prev2 = Review(1);
                if (previous.Type == eTokenType.Symbol)
                    visit = previous;
                else if (prev2 != null && prev2.Type == eTokenType.Symbol)
                    visit = prev2;
                if (visit != null)
                {
                    switch (visit.Content)
                    {
                        case "+":
                        case "-":
                        case "*":
                        case "/":
                        case "^":
                            deny = false;
                            break;
                    }
                }
            }

            if (Buffer.Length == 0 && startChars.Contains(c) && next.HasValue && startChars.Contains(next.Value) && !deny)
                return true;
            else if (numChars.Contains(c))
                return true;
            else if (c.Equals(DecimalChar) && !Buffer.Contains(c))
                return true;
            else if (c.Equals(ThousandSeparator))
                return true;
            else
                return false;
        }

        /// <summary>
        /// Creates the token based on the current input-pattern.
        /// </summary>
        /// <returns>
        /// An IToken containing the current input-pattern.
        /// </returns>
        protected override IToken CreateToken()
        {
            if (Buffer.Contains(ThousandSeparator))
            {
                IEnumerable<char> rBuffer = null;
                if (Buffer.Contains(DecimalChar))
                {
                    rBuffer = Buffer.Substring(0, Buffer.IndexOf(DecimalChar)).Reverse();
                }
                else
                {
                    rBuffer = Buffer.Reverse();
                }
                for (int count = 1; count <= rBuffer.Count(); count++)
                {
                    if (count % 4 == 0)
                    {
                        char c = rBuffer.ElementAt(count - 1);
                        if (!c.Equals(ThousandSeparator))
                            return new Token(Buffer, eTokenType.InvalidNumberLiteral);
                    }
                }
            }
            if (RemovePositiveSign && Buffer.StartsWith("+"))
                return new Token(Buffer.Substring(1), eTokenType.NumberLiteral);
            else
                return new Token(Buffer, eTokenType.NumberLiteral);
        }

        #endregion
    }
}